package com.sisyphus.tron;

import android.content.Context;

import com.sisyphus.tron.persistence.AccountEntity;
import com.sisyphus.tron.persistence.TronCliDatabase;
import com.sisyphus.tron.persistence.TronDao;
import com.sisyphus.tron.transaction.TRC10TransactionBuilder;
import com.sisyphus.tron.transaction.TRXTransactionBuilder;
import com.sisyphus.tron.transaction.TransactionBuilder;

import org.spongycastle.util.encoders.Hex;
import org.tron.common.crypto.ECKey;
import org.tron.common.crypto.Hash;
import org.tron.common.utils.Base58;

import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;


/**
 * 创建的实例。 除了实用程序函数之外，它还囊括了所有相关模块
 */
public class TronAndroid {

    //
    //  ------------------------------------------------------------------------
    // |                                工具方法                                 |
    //  ------------------------------------------------------------------------
    //

    /**
     * 将Hex字符转码为byte
     */
    public static byte[] decode(String data) {
        return Hex.decode(data);
    }


    /**
     * 字符转化为hex
     */
    public static String toHex(byte[] data) {

        return Hex.toHexString(data);
    }


    /**
     * 转码为base58
     */
    public static String encode58Check(byte[] input) {
        byte[] hash0 = Hash.sha256(input);
        byte[] hash1 = Hash.sha256(hash0);
        byte[] inputCheck = new byte[input.length + 4];
        System.arraycopy(input, 0, inputCheck, 0, input.length);
        System.arraycopy(hash1, 0, inputCheck, input.length, 4);
        return Base58.encode(inputCheck);
    }


    /**
     * 解码为base58
     */
    public static byte[] decode58Check(String input) {
        byte[] decodeCheck = Base58.decode(input);
        if (decodeCheck.length <= 4) {
            return new byte[]{};
        }
        byte[] decodeData = new byte[decodeCheck.length - 4];
        System.arraycopy(decodeCheck, 0, decodeData, 0, decodeData.length);
        byte[] hash0 = Hash.sha256(decodeData);
        byte[] hash1 = Hash.sha256(hash0);
        if (hash1[0] == decodeCheck[decodeData.length]
                && hash1[1] == decodeCheck[decodeData.length + 1]
                && hash1[2] == decodeCheck[decodeData.length + 2]
                && hash1[3] == decodeCheck[decodeData.length + 3]) {
            return decodeData;
        }
        return new byte[]{};
    }


    /**
     * 验证私钥格式
     */
    public static boolean priKeyValid(String privateKey) {
        ECKey ecKey = ECKey.fromPrivate(new BigInteger(privateKey, 16));
        byte[] priKey = ecKey.getPrivKeyBytes();

        if (priKey.length != 32) {
            System.out.println("Warning: PrivateKey length need 64 but " + priKey.length + " !!");
            return false;
        }

        return true;
    }


    //
    //  ------------------------------------------------------------------------
    // |                                账号方法                                 |
    //  ------------------------------------------------------------------------
    //

    private TronDao mTronDao;

    private String fullNode, solidityNode;


    public TronAndroid(Context context) {
        this(context, "grpc.trongrid.io:50051", null);
    }


    public TronAndroid(Context context, String fullNode, String solidityNode) {
        this.fullNode = fullNode;
        this.solidityNode = solidityNode;
        this.mTronDao = TronCliDatabase.getInstance(context).tronDao();
    }


    /**
     * 创建钱包
     */
    public Account createAccount() {

        return new Account();
    }


    /**
     * 根据私钥创建钱包
     */
    public Account createAccount(String privateKey) {

        return new Account(null, privateKey);
    }


    /**
     * 保存账号
     *
     * @param password 密码
     * @param alias    别名
     */
    public void saveAccount(String password, String alias, Account account) {
        this.saveAccount(password, alias, false, account);
    }


    /**
     * 获取
     */
    public Account find(String address) {
        AccountEntity accountEntity = mTronDao.findByAddress(address);
        return new Account(accountEntity.getAlias(), accountEntity.getPrivateKey());
    }


    /**
     * 修改
     */
    public void update(String address, String password, String alias) {

        AccountEntity accountEntity = mTronDao.findByAddress(address);

        if (accountEntity == null) {
            throw new NullPointerException();
        }

        mTronDao.update(
                new AccountEntity(
                        accountEntity.getPublicKey(),
                        accountEntity.getPrivateKey(),
                        address,
                        password == null ? accountEntity.getPassword() : password,
                        alias == null ? accountEntity.getAlias() : alias
                )
        );

    }


    /**
     * 删除
     */
    public void delete(String address) {
        mTronDao.deleteAccount(new AccountEntity(address));
    }


    /**
     * 保存账号
     *
     * @param password 密码
     * @param alias    别名
     * @param isForced 是否强制
     */
    public void saveAccount(String password, String alias, boolean isForced, Account account) {
        // 强制保存并本地已有相同账户时，保存失败
        if (!isForced && mTronDao.findByAddress(account.getAddress()) != null) {
            throw new IllegalStateException("已存在相同钱包");
        }

        mTronDao.addAccount(
                new AccountEntity(
                        account.getPublicKey(),
                        account.getPrivateKey(),
                        account.getAddress(),
                        password,
                        alias
                )
        );
    }


    /**
     * 导出账号
     */
    public Account exportAccount(String password, String address) {
        AccountEntity accountEntity = mTronDao.findByAddressAndPassword(address, password);

        if (accountEntity == null) {
            throw new IllegalStateException("没有找到");
        }

        return new Account(
                accountEntity.getAlias(),
                accountEntity.getPrivateKey()
        );
    }


    /**
     * 所有账号
     */
    public List<Account> accounts() {
        return mTronDao.allAccount()
                .stream()
                .map(accountEntity ->
                        new Account(
                                accountEntity.getAlias(),
                                accountEntity.getPrivateKey()
                        )
                )
                .collect(Collectors.toList());
    }


    /**
     * 发起交易, TRX
     */
    public TransactionBuilder transactionBuilder(String hexTo, String hexFrom, Long amount) {

        return new TRXTransactionBuilder(hexTo, hexFrom, amount, fullNode, solidityNode);
    }


    /**
     * 发起交易, TRC10
     */
    public TransactionBuilder transactionBuilder(String hexTo, String hexFrom, String tokenId, Long amount) {

        return new TRC10TransactionBuilder(hexTo, hexFrom, tokenId, amount);
    }

}
